{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2 Namespaces and Functions 从命名空间到函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1 Functions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The definition of a function adds its name to the namespace."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(a, b):\n",
    "    return a + b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function __main__.add(a, b)>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add.__qualname__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add.__qualname__ = 'multiply'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "multiply"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add.__qualname__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "multiply = add"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "multiply"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add.__dict__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add.spam = 'eggs'\n",
    "add.__dict__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Function execution defines a new scope.  When the function is\n",
    "called, a namespace is created and the parameter names are bound to\n",
    "the function call's arguments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(a, b):\n",
    "    return a + b\n",
    "add(2, 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Note that default argument values are expressions evaluated when the function is defined."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def f(p1=print('When are default args evaluated?')):\n",
    "    print(f'p1 has value {p1}')\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def f1(p1=print('p1 defined')):\n",
    "    def f2(p2=print('p2 defined')):\n",
    "        pass\n",
    "    f2()\n",
    "    return 'from f1'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f1()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f1()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f1.__code__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f1.__code__.co_consts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f1.__code__.co_consts[2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import dis\n",
    "dis.dis(f1.__code__.co_consts[2])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dis.dis(f1.__code__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Function annotations are arbitrary expressions.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(a: int, b: int, verbose: bool) -> int:\n",
    "    return a + b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add.__annotations__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "type(add.__annotations__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(a: len('abcd'), b: 99 + 1, verbose: object()) -> 'A string literal':\n",
    "    return a + b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add.__annotations__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2 Exercises: Functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def f(*args, **kwargs):\n",
    "    print(f'{args!r}\\n{kwargs!r}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f(1, 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f(1, a=3, b=4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "t = 1, 2\n",
    "t"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "d = dict(a=3, b=4)\n",
    "d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f(t)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f(d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f(*t)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f(*d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f(**t)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f(**d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.3 Scopes and Search Order"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Name binding operations covered so far:\n",
    "\n",
    "  - *name* `=` (assignment)\n",
    "  - `del` *name* (unbinds the name)\n",
    "  - `def` *name* function definition (including lambdas)\n",
    "  - `def name(`*names*`):` (function execution)\n",
    "  - *name*`.`*attribute_name* `=`, `__setattr__`, `__delattr__`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "Review:\n",
    "- A *namespace* is a mapping from names to objects.\n",
    "  Think of it as a dictionary.\n",
    "\n",
    "- Simple assignment (`a_name =`) and simple `del` (`del\n",
    "  an_identifier`) of a name are namespace operations, not operations\n",
    "  on objects. 我们采用 等号和`del`操作符用来绑定和删除名称\n",
    "\n",
    "Terminology and Definitions:\n",
    "- A *scope* is a section of Python code where a namespace is *directly*\n",
    "  accessible, by using a *name*. 一个`scope`则是python的一个区域，在这个区域内可以直接访问，在这个区域外，你得使用`名字`\n",
    "\n",
    "- For an *indirectly* accessible namespace you access values via dot\n",
    "  notation, e.g. `math.pi` or `sys.version_info.major`.\n",
    "\n",
    "- 当我们`直接`访问名字的时候，遵循Python的规范顺序 (from http://docs.python.org/3/tutorial): \n",
    "\n",
    "  - The innermost scope contains local names\n",
    "\n",
    "  - The namespaces of enclosing functions, searched starting\n",
    "    with the nearest enclosing scope; (or the module if outside any\n",
    "    function)\n",
    "\n",
    "  - The middle scope contains the current module's global names\n",
    "\n",
    "  - The outermost scope is the namespace containing built-in\n",
    "    names 最外层是内置(buildin)\n",
    "![image](namespace.jpg)\n",
    "\n",
    "- All namespace *changes* happen in the local scope (i.e. in the\n",
    "  current scope in which the namespace-changing code executes). 命名空间的改变只发生在局部\n",
    "  - name =   这是赋值\n",
    "  - del name  删除名字\n",
    "  - import name 引入包 也是引入名字\n",
    "  - def name 定义也是定义名字\n",
    "  - class name 创建类 也是一种特殊的def\n",
    "  - 函数的参数同样也是定义名字 比如 def func(name)\n",
    "  - 异常也是定义名字 Exception as name\n",
    "  - with 语法 也是定义名字 with open(filename) as name\n",
    "  - docstrings 也是定义名字 __doc__\n",
    "\n",
    "  You should avoid reassigning built-in names because it may mislead\n",
    "  the reader, but let's do so to explore how name scopes work:\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们来举一个例子\n",
    "\n",
    "![image](namespace_code.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "让我们再来看一个例子\n",
    "\n",
    "![image](namespace_code2.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "你不应该修改内置的名字，比如说`len`（这里是一个内置函数）。我们试试看"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function len(obj, /)>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def f():\n",
    "    print('Line A', len)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Line A <built-in function len>\n"
     ]
    }
   ],
   "source": [
    "f()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def f():\n",
    "    def len():\n",
    "        pass\n",
    "    print(len)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<function f.<locals>.len at 0x000001FF445158C8>\n"
     ]
    }
   ],
   "source": [
    "f()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "def f():\n",
    "    def len():\n",
    "        print('Line B', len)\n",
    "        pass\n",
    "    len()\n",
    "    print('Line C', len)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Line B <function f.<locals>.len at 0x000001FF44515400>\n",
      "Line C <function f.<locals>.len at 0x000001FF44515400>\n"
     ]
    }
   ],
   "source": [
    "f()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "def f():\n",
    "    def len():\n",
    "        len = 'short'\n",
    "        print('Line E', len)\n",
    "    print('Line F', len)\n",
    "    len()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Line F <function f.<locals>.len at 0x000001FF44515AE8>\n",
      "Line E short\n"
     ]
    }
   ],
   "source": [
    "f()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function len(obj, /)>"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "len = 99"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "99"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "def print_len(s):\n",
    "    print('len(s):', len(s))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "'int' object is not callable",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-33-be95f061ee45>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mprint_len\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'walk'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-32-0fdb545d591d>\u001b[0m in \u001b[0;36mprint_len\u001b[1;34m(s)\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mprint_len\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m     \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'len(s):'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: 'int' object is not callable"
     ]
    }
   ],
   "source": [
    "print_len('walk')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "99"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "del len"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function len(obj, /)>"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'len' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-38-359eb095d8a5>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32mdel\u001b[0m \u001b[0mlen\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m: name 'len' is not defined"
     ]
    }
   ],
   "source": [
    "del len"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function len(obj, /)>"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "len(s): 4\n"
     ]
    }
   ],
   "source": [
    "print_len('walk')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [],
   "source": [
    "pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "invalid syntax (<ipython-input-42-73f8893aeef5>, line 1)",
     "output_type": "error",
     "traceback": [
      "\u001b[1;36m  File \u001b[1;32m\"<ipython-input-42-73f8893aeef5>\"\u001b[1;36m, line \u001b[1;32m1\u001b[0m\n\u001b[1;33m    pass = 3\u001b[0m\n\u001b[1;37m         ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m invalid syntax\n"
     ]
    }
   ],
   "source": [
    "pass = 3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False None True and as assert async await break class continue def del elif else except finally for from global if import in is lambda nonlocal not or pass raise return try while with yield\n"
     ]
    }
   ],
   "source": [
    "import keyword\n",
    "print(' '.join(keyword.kwlist))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Keywords are the only identifiers (not names) that don't refer to\n",
    "objects, and they don't really exist as keywords at run-time (except\n",
    "inside `eval` which is compile-time). 关键字不是名称，所以关键字不会指向对象！\n",
    "\n",
    "\n",
    "Keywords at https://docs.python.org/3/reference/lexical_analysis.html#keywords\n",
    "\n",
    "    False     class     finally   is        return\n",
    "    None      continue  for       lambda    try\n",
    "    True      def       from      nonlocal  while\n",
    "    and       del       global    not       with\n",
    "    as        elif      if        or        yield\n",
    "    assert    else      import    pass\n",
    "    break     except    in        raise"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.4 Function Locals"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面让我们来试试神奇的代码吧！ 让我们来踩一踩坑。下面的编程错误（但是几乎每个python程序员都犯过）至少价值几个亿（的损失）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [],
   "source": [
    "value = 'module'\n",
    "def test_outer_scope():\n",
    "    print(value)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "module\n"
     ]
    }
   ],
   "source": [
    "test_outer_scope()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_local():\n",
    "    value = 'inner'\n",
    "    print(value)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'module'"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "inner\n"
     ]
    }
   ],
   "source": [
    "test_local()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_unbound_local():\n",
    "    print(value)\n",
    "    value = 'inner'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'module'"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "ename": "UnboundLocalError",
     "evalue": "local variable 'value' referenced before assignment",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mUnboundLocalError\u001b[0m                         Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-56-7f2b3750ecf3>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mtest_unbound_local\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-54-3050560a0bb4>\u001b[0m in \u001b[0;36mtest_unbound_local\u001b[1;34m()\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mtest_unbound_local\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m     \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      3\u001b[0m     \u001b[0mvalue\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;34m'inner'\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mUnboundLocalError\u001b[0m: local variable 'value' referenced before assignment"
     ]
    }
   ],
   "source": [
    "test_unbound_local()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'module'"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "value"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Let's look at the function `test_unbound_local` to help us understand this error."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_unbound_local.__code__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_unbound_local.__code__.co_argcount  # count of positional args"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_unbound_local.__code__.co_name  # function name (3 places)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_unbound_local.__code__.co_names  # names used in bytecode"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_unbound_local.__code__.co_nlocals  # number of locals"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_unbound_local.__code__.co_varnames  # names of locals"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  See \"Code objects\" at https://docs.python.org/3/reference/datamodel.html?highlight=co_nlocals#the-standard-type-hierarchy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_local():\n",
    "    value = 'inner'\n",
    "    print(value)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_unbound_local():\n",
    "    print(value)\n",
    "    value = 'inner'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_local.__code__.co_consts, test_local.__code__.co_consts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('test_local')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dis.dis(test_local.__code__.co_code)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('test_unbound_local')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dis.dis(test_unbound_local.__code__.co_code)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [],
   "source": [
    "import dis"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import io"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import itertools"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def dis_column(obj):\n",
    "    str_file = io.StringIO()\n",
    "    dis.dis(obj, file=str_file)\n",
    "    return [line[14:] for line in str_file.getvalue().splitlines()]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def columnar(col1, col2):\n",
    "    width = max(len(row) for row in col1)\n",
    "    [f'{s1:{width}}   {s2}' for s1, s2 in itertools.zip_longest(col1, col2)]\n",
    "    print('\\n'.join(f'{s1:{width}}   {s2}' for s1, s2 in itertools.zip_longest(col1, col2)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "columnar(dis_column(test_local), dis_column(test_unbound_local))    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Same bytecode, but different order - LOAD_FAST before STORE_FAST is the problem."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "> \"This is because when you make an assignment to a variable in a\n",
    "> scope, that variable becomes local to that scope and shadows any\n",
    "> similarly named variable in the outer scope. Since the last\n",
    "> statement in foo assigns a new value to x, the compiler recognizes\n",
    "> it as a local variable. Consequently when the earlier print x\n",
    "> attempts to print the uninitialized local variable and an error\n",
    "> results.\" --\n",
    "> https://docs.python.org/3/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value\n",
    "\n",
    "\n",
    "> 这是因为当你做赋值=操作的时候，这个变成了局部变量，而这个是预先编译的（执行的def）\n",
    "> 所以当你真的执行的时候，你要的是全局的，所以就会说找不到。\n",
    ">  >   python社区坚持认为这不是错误，应当如此害你！"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  To explore this further on your own compare these two:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [],
   "source": [
    "import codeop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  1 -->       0 LOAD_NAME                0 (dis)\n",
      "              2 LOAD_METHOD              0 (dis)\n",
      "              4 LOAD_NAME                1 (codeop)\n",
      "              6 LOAD_METHOD              2 (compile_command)\n",
      "              8 LOAD_CONST               0 ('def t1(): a = b; b = 7')\n",
      "             10 CALL_METHOD              1\n",
      "             12 CALL_METHOD              1\n",
      "             14 PRINT_EXPR\n",
      "             16 LOAD_CONST               1 (None)\n",
      "             18 RETURN_VALUE\n"
     ]
    }
   ],
   "source": [
    "dis.dis(codeop.compile_command('def t1(): a = b; b = 7'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dis.dis(codeop.compile_command('def t2(): b = 7; a = b'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  What about `global`?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_global():\n",
    "    # print('A -->', value)\n",
    "    global value\n",
    "    print('B -->', value)\n",
    "    value = 'inner'  # This assignment is what makes value local\n",
    "    print('C -->', value)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_global()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "value"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_global.__code__.co_varnames"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Note LOAD_GLOBAL instead of LOAD_FAST:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dis.dis(test_global.__code__.co_code)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.5 Exercises: Function Non-Locals"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Python 3 added `nonlocal`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_nonlocal():\n",
    "    x = 5\n",
    "    def assign_6():\n",
    "        nonlocal x\n",
    "        print(2, x)\n",
    "        x = 6\n",
    "        print(3, x)\n",
    "    print(1, x)\n",
    "    assign_6()\n",
    "    print(4, x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = 'module'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_nonlocal()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def f():\n",
    "    print(x)\n",
    "    def x():\n",
    "        pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def f2():\n",
    "    del len"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f2()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  See also https://docs.python.org/3/tutorial/classes.html#scopes-and-namespaces-example"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.6 Built-ins"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Restart Python to unclutter the namespace.\n",
    "让我们重启一下重置namespace 看看"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "IPython.notebook.kernel.restart();\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%javascript\n",
    "IPython.notebook.kernel.restart();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['In', 'Out', 'exit', 'get_ipython', 'quit']"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[n for n in dir() if not n.startswith('_')]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  There are lots of built-in names that `dir()` doesn't show us.\n",
    "Let's use some Python to explore all the builtin names by category."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import builtins, collections, inspect, textwrap\n",
    "fill = textwrap.TextWrapper(width=60).fill\n",
    "def pfill(pairs):\n",
    "    \"\"\"Sort and print first of every pair\"\"\"\n",
    "    print(fill(' '.join(list(zip(*sorted(pairs)))[0])))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Collect all members of `builtins`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "members = set([\n",
    "    m for m in inspect.getmembers(builtins)\n",
    "    if not m[0].startswith('_')])\n",
    "len(members)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Pull out only the `exception` types:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "exceptions = [\n",
    "    (name, obj) for (name, obj) in members\n",
    "    if inspect.isclass(obj) and\n",
    "    issubclass(obj, BaseException)]\n",
    "members -= set(exceptions)\n",
    "len(exceptions), len(members)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pfill(exceptions)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://docs.python.org/3/library/exceptions.html#exception-hierarchy:\n",
    "\n",
    "    BaseException\n",
    "     +-- SystemExit\n",
    "     +-- KeyboardInterrupt\n",
    "     +-- GeneratorExit\n",
    "     +-- Exception           <---- NB\n",
    "          +-- StopIteration\n",
    "          +-- StopAsyncIteration\n",
    "          +-- ArithmeticError\n",
    "          |    +-- FloatingPointError\n",
    "          |    +-- OverflowError\n",
    "          |    +-- ZeroDivisionError\n",
    "          +-- AssertionError\n",
    "          +-- AttributeError\n",
    "          +-- BufferError\n",
    "          +-- EOFError\n",
    "          +-- ImportError\n",
    "               +-- ModuleNotFoundError\n",
    "          +-- LookupError\n",
    "          |    +-- IndexError\n",
    "          |    +-- KeyError\n",
    "          +-- MemoryError\n",
    "          +-- NameError\n",
    "          |    +-- UnboundLocalError\n",
    "          +-- OSError\n",
    "          |    +-- BlockingIOError\n",
    "          |    +-- ChildProcessError\n",
    "          |    +-- ConnectionError\n",
    "          |    |    +-- BrokenPipeError\n",
    "          |    |    +-- ConnectionAbortedError\n",
    "          |    |    +-- ConnectionRefusedError\n",
    "          |    |    +-- ConnectionResetError\n",
    "          |    +-- FileExistsError\n",
    "          |    +-- FileNotFoundError\n",
    "          |    +-- InterruptedError\n",
    "          |    +-- IsADirectoryError\n",
    "          |    +-- NotADirectoryError\n",
    "          |    +-- PermissionError\n",
    "          |    +-- ProcessLookupError\n",
    "          |    +-- TimeoutError\n",
    "          +-- ReferenceError\n",
    "          +-- RuntimeError\n",
    "          |    +-- NotImplementedError\n",
    "          |    +-- RecursionError\n",
    "          +-- SyntaxError\n",
    "          |    +-- IndentationError\n",
    "          |         +-- TabError\n",
    "          +-- SystemError\n",
    "          +-- TypeError\n",
    "          +-- ValueError\n",
    "          |    +-- UnicodeError\n",
    "          |         +-- UnicodeDecodeError\n",
    "          |         +-- UnicodeEncodeError\n",
    "          |         +-- UnicodeTranslateError\n",
    "          +-- Warning\n",
    "               +-- DeprecationWarning\n",
    "               +-- PendingDeprecationWarning\n",
    "               +-- RuntimeWarning\n",
    "               +-- SyntaxWarning\n",
    "               +-- UserWarning\n",
    "               +-- FutureWarning\n",
    "               +-- ImportWarning\n",
    "               +-- UnicodeWarning\n",
    "               +-- BytesWarning\n",
    "               +-- ResourceWarning"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'pfill' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-44-ff72fa6e3f49>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mpfill\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mmembers\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m: name 'pfill' is not defined"
     ]
    }
   ],
   "source": [
    "pfill(members)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  If you want to catch all exceptions use except Exception, not bare except:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    1/0\n",
    "except Exception as e:\n",
    "    print('The exception raised was', e)\n",
    "    raise"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Most are either of type `type`, or `builtin_function_or_method`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "type(int), type(len)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Print them:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bnames = collections.defaultdict(set)\n",
    "for name, obj in members:\n",
    "    bnames[type(obj)].add((name, obj))\n",
    "for typ in [type(int), type(len)]:\n",
    "    pairs = bnames.pop(typ)\n",
    "    print(typ)\n",
    "    pfill(pairs)\n",
    "    print()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  The leftovers:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for typ, pairs in bnames.items():\n",
    "    print('{}: {}'.format(typ, ' '.join((n for (n, o) in pairs))))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
